#include "serveur_reseau.h"
#include "reseau.h"
#include "commun/erreur.h"
#include "commun/sous_systeme.h"
#include "temps/minuterie.h"
#include <sstream>
#include <string>
#include <algorithm>

bool serveur_reseau::occupe() const { return occupe_; }

/**
 * Alloue les sockets et tout le tralala
 */
serveur_reseau::serveur_reseau(int port_serveur) : occupe_(false), adresse_client_(0), recepteur_(0), emetteur_(0), port_udp_serveur_(port_serveur) {
    // Résolution de notre propre nom (mode d'écoute)
    if(SDLNet_ResolveHost(&adresse_serveur_, NULL, port_serveur) < 0)
        throw une_erreur<resolution_nom_hote>();
    
    // Ouverture d'une connexion serveur ("listen") par TCP
    chaussette_tcp_ecoute_ = SDLNet_TCP_Open(&adresse_serveur_);
    if(!chaussette_tcp_ecoute_)
        throw une_erreur<ouverture_socket>();
    
    // Ouverture d'un assortiment de chaussettes
    assortiment_de_chaussettes_ = SDLNet_AllocSocketSet(1);
    if(!assortiment_de_chaussettes_) {
        // Si erreur...
        SDLNet_TCP_Close(chaussette_tcp_ecoute_);
        // erreur à lancer
        throw une_erreur<ouverture_socket>();
    }
}
    
/**
 * Accepte les connexions de manière bloquante et initie la connexion
 * TCP puis UDP grâce à un protocole particulier.
 */
void serveur_reseau::accepter_connexions() {
    if(occupe_) return;
    
    chaussette_tcp_client_ = SDLNet_TCP_Accept(chaussette_tcp_ecoute_);
    
    // Un nouveau client entre dans la boutique
    if(chaussette_tcp_client_) {
        // Obtention de l'adresse IP du nouveau client
        adresse_client_ = SDLNet_TCP_GetPeerAddress(chaussette_tcp_client_);
        if(!adresse_client_) {
            deconnecter();
            throw une_erreur<ouverture_socket>();
        }
        
        // Ajout de la chaussette TCP client à l'assortiment de chaussettes à suivre
        if(SDLNet_TCP_AddSocket(assortiment_de_chaussettes_, chaussette_tcp_client_) < 0) {
            // Si erreur...
            deconnecter();
            // erreur à lancer
            throw une_erreur<ouverture_socket>();
        }
		
		chaussette_udp_ = SDLNet_UDP_Open(0);
		if(!chaussette_udp_) {
			deconnecter();
			throw une_erreur<ouverture_socket>();
		 }
		
		adresse_client_udp_.host = adresse_client_->host;
		adresse_client_udp_.port = SDL_SwapBE16(port_udp_serveur_);
		
		// Instanciation d'un émetteur et d'un recepteur
		recepteur_ = new recepteur_reseau(chaussette_tcp_client_, chaussette_udp_, assortiment_de_chaussettes_);
		emetteur_ = new emetteur_reseau(chaussette_tcp_client_, chaussette_udp_, adresse_client_udp_);
        occupe_ = true;
		
		// Envoi d'un paquet UDP bidon pour le port
		Uint16 valeur_bidon = 0;
		{
			char ack[2] = "S";
			minuterie<> timeout(1000);
			do {
				emetteur_->envoyer_udp(ack, 2);
			} while(!recepteur_->recevoir_port_udp(valeur_bidon) && !timeout); 
		}
		
		if(valeur_bidon == 0)
			throw une_erreur<protocole_connexion>();
	}
}

/**
 * Déconnecte le client connecté actuellement (ne réécoute pas)
 */
void serveur_reseau::deconnecter() {
    if(!occupe_) return;
	delete recepteur_;
	delete emetteur_;	
    SDLNet_TCP_DelSocket(assortiment_de_chaussettes_, chaussette_tcp_client_);
	if(chaussette_udp_) 
		SDLNet_UDP_Close(chaussette_udp_);
	if(chaussette_tcp_client_)
		SDLNet_TCP_Close(chaussette_tcp_client_);}

/**
 * "Destruit" et libère les ressources
 */
serveur_reseau::~serveur_reseau() throw() {
    // Si la connexion était établie, il faut la couper
    if(occupe_) 
		deconnecter();
    // Libération de l'assortiment de chaussettes
    SDLNet_FreeSocketSet(assortiment_de_chaussettes_);
    // Libération des chaussettes
    SDLNet_TCP_Close(chaussette_tcp_ecoute_);
}

